# Java 中 String、StringBuffer、StringBuilder 的用法与区别
# 一、区别
# 1.1 可变性
String
是不可变的。
String s = new String("Hello");
System.out.println("s = " + s); // output: s = Hello
// 转为小写
s = s.toLowerCase();
System.out.println("s = " + s); // output: s = hello
1
2
3
4
5
2
3
4
5
咦,你不是说 String
是不可变的吗?这怎么就变了。s
变为小写字母了。
String 不可变指的是 String 指向的对象不可变,而不是 String 对象引用不可变。
s
是一个对象引用,存放 Hello
的地址,s
存在栈中。
Hello
是字符串对象,它的值不会发生改变,存在堆中。
Hello
字符串没有被改变,只是新建了一个新的 hello
字符串,然后 s
这个引用指向了新的。
Hello
字符串后面会被垃圾回收处理。
# 1.2 线程安全性
String
不可变,线程安全。
StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。
# 1.3 性能
每次对 String
对象进行改变的时候,都会生成一个新的 String
对象,然后将引用指向新的 String
对象,然后丢弃掉旧的 String
对象,因此效率很差。
StringBuilder
会预分配缓冲区,每次只会对 StringBuilder
对象本身进行操作,而不是生成新的对象并改变对象引用。因此效率比较高。
测试如下:
// 获取开始时间
long startTime = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 1000; i++) {
s = s + "," + i;
}
System.out.println(s);
// 获取结束时间
long endTime = System.currentTimeMillis();
// output: time consuming: 11ms
System.out.println("time consuming: "+ (endTime - startTime) + "ms");
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
// 获取开始时间
long startTime = System.currentTimeMillis();
StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 1000; i++) {
sb.append(',');
sb.append(i);
}
String s = sb.toString();
System.out.println(s);
// 获取结束时间
long endTime = System.currentTimeMillis();
// output: time consuming: 1ms
System.out.println("time consuming: "+ (endTime - startTime) + "ms");
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
一个 11 ms,一个 1 ms,效率孰高孰低,一眼便知。
# 二、String 不可变的原因
# 2.1 String 源码分析
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
@Stable
private final byte[] value;
}
1
2
3
4
5
6
2
3
4
5
6
final
修饰 String
类,表明 String
类不能被继承,因此没有子类。
final
修饰字段:
- 字段是基本类型,那么字段不可变
- 字段是引用类型,那么引用的对象不可变
因此很多人就认为 final
修饰了 value
就是不可变的原因,错错错,大错特错。
byte[]
是个数组类型,数组类型是引用类型,只能说明 value
不能指向其他对象, value
本身还是可以修改的。
final byte[] value = {1, 2, 3};
byte[] temp = {4, 5, 6};
value = temp;
// error: java: 无法为最终变量value分配值
1
2
3
4
2
3
4
final byte[] value = {1, 2, 3};
value[0] = 4;
for(byte b: value) {
System.out.println(b);
}
// output: 4 2 3
1
2
3
4
5
6
2
3
4
5
6
其实不可变的原因是:String
类中的各个方法都没有去改变 String
对象的值,都是去 new String()
产生一个新的对象出来。然后 final
修饰类,也没有子类去改变不可变的状态。